package cn.com.miao.lynx.common.dict.aspect;

import cn.com.miao.lynx.common.core.model.Result;
import cn.com.miao.lynx.common.core.util.CglibUtil;
import cn.com.miao.lynx.common.core.util.ReflectTool;
import cn.com.miao.lynx.common.dict.annotation.Dict;
import cn.com.miao.lynx.common.dict.annotation.DictAssembly;
import cn.com.miao.lynx.common.dict.api.DictParam;
import cn.com.miao.lynx.common.dict.api.DictResult;
import cn.com.miao.lynx.common.dict.api.RemoteDictService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * DictAspect
 * 字典切面
 * @title: DictAspect
 * @description:
 * @author: dengmiao
 * @create: 2019-12-29 15:42
 **/
@Slf4j
@AllArgsConstructor
@Aspect
public class DictAspect {

    private final RemoteDictService dictService;

    @Around("@annotation(dictAssembly)")
    @SneakyThrows
    public Object dictAfterReturning(ProceedingJoinPoint pjp, DictAssembly dictAssembly) {
        Object result = pjp.proceed();
        result = this.assemblyFromResponseEntity(result, dictAssembly);
        return result;
    }

    private Object assemblyFromResponseEntity(Object result, DictAssembly dictAssembly) {
        if (result instanceof ResponseEntity) {
            ResponseEntity<?> responseEntity = (ResponseEntity<?>) result;
            result = responseEntity.getBody();
            result = assemblyFromResult(result, dictAssembly);
            return new ResponseEntity(result, responseEntity.getStatusCode());
        }
        return assemblyFromResult(result, dictAssembly);
    }

    private Object assemblyFromResult(Object result, DictAssembly dictAssembly) {
        // 处理result标准返回数据
        if (result instanceof Result) {
            Result r = (Result) result;
            Object data = r.getResult();
            r.setResult(assertType(data, dictAssembly));
        }
        // 非result
        else {
            result = assertType(result, dictAssembly);
        }
        return result;
    }

    public Object assertType(Object result, final DictAssembly dictAssembly) {
        // data是集合/Page/单个对象
        if (result instanceof Page) {
            Page page = (Page) result;
            page = page.map(it -> assemblyDict(it, dictAssembly));
            result = page;
        }
        else if (result instanceof Collection) {
            Collection c = (Collection) result;
            c = (Collection) c.parallelStream().map(item -> assemblyDict(item, dictAssembly)).collect(Collectors.toCollection(LinkedList::new));
            result = c;
        }
        else {
            result = assemblyDict(result, dictAssembly);
        }
        return result;
    }

    private Object assemblyDict(Object data, final DictAssembly dictAssembly) {
        // 处理map 添加键值
        if (data instanceof Map) {
            Dict[] dictVal = dictAssembly.value();
            Map map = (Map) data;
            for (Dict dict : dictVal) {
                String dictCode = dict.dicCode(), dictProp = dict.dictProp(), dictField = dict.dictField(), tableName = dict.tableName();
                if (StrUtil.isNotBlank(dictProp)) {
                    if (StrUtil.isBlank(tableName)) {
                        DictParam dictParam = DictParam.builder().code(dictCode).value(MapUtil.getStr(map, dictProp)).build();
                        DictResult dictItem = dictService.loadDictByCode(dictParam);
                        if (dictItem != null) {
                            map.put(dictField, dictItem.getDictText());
                        }
                    }
                }
            }
            return map;
        }
        // 处理普通bean对象 生成代理对象（继承源对象）并添加属性及赋值
        else {
            // 属性类型map
            Map<String, Class> propertyMap = Maps.newHashMap();
            // 属性值map
            Map<String, Object> valueMap = Maps.newHashMap();
            Class<?> aClass = data.getClass();
            final ReflectTool reflectTool = ReflectTool.reflectToolFactory(data);
            Arrays.stream(ReflectUtil.getFieldsDirectly(aClass, true))
                    .filter(field -> field.isAnnotationPresent(Dict.class)).collect(Collectors.toList())
                    .forEach(field -> {
                        Dict dict = field.getAnnotation(Dict.class);
                        String dictCode = dict.dicCode(), dictField = dict.dictField(), tableName = dict.tableName();
                        String fieldName = field.getName();
                        Object val = reflectTool.getter(fieldName, field.getType());
                        try {
                            propertyMap.put(dictField, Class.forName("java.lang.String"));
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                        if (val != null) {
                            String fieldValue = String.valueOf(val);
                            if (StrUtil.isNotBlank(dictField)) {
                                // 判断tableName是否空
                                if (StrUtil.isBlank(tableName)) {
                                    DictParam dictParam = DictParam.builder().code(dictCode).value(fieldValue).build();
                                    DictResult dictItem = dictService.loadDictByCode(dictParam);
                                    if (dictItem != null) {
                                        valueMap.put(dictField, dictItem.getDictText());
                                    }
                                }
                            }
                        }
                    });
            Object generate = CglibUtil.generate(propertyMap, valueMap, aClass);
            BeanUtil.copyProperties(data, generate);
            return generate;
        }
    }
}
